/**
 * Created by xinxinran on 2018/2/1.
 */
'use strict';
const base = require('../socket/baseClass'),
    daoUtil = require('../dao'),
    commonUtil = require('../utils/commonUtil'),
    beanUtil = require('../utils/beanUtil');

/**
 * Created by xinxinran on 2017/7/13.
 */
'use strict';

const JspApiService = base.BaseService.extend({
    init: function () {
        this.id = 'JspApiService';
        this.socketSender = null;
        this.deviceIndex = {};
        this.socketIndex = {};
        this.finalJspData = {};
        this.jspDevIndex = {};
        this.offlineIndex = {};
    },

    regDevice : function(ctx){
        let params = ctx.params,
            deviceId = params.mac,
            deviceInfo = {
                id : deviceId,
                socketId : params._socketId,
                daoName : params.daoName,
                computerName : params.computerName
            };
        this.deviceIndex[deviceId] = deviceInfo;
        this.socketIndex[params._socketId] = deviceInfo;

        if(this.offlineIndex[deviceId]){
            this.jspDevIndex[deviceId] = this.offlineIndex[deviceId];
            delete this.offlineIndex[deviceId];
            this.jspDevIndex[deviceId].forEach(client => client.notifyOnLine());
        }

        return {code : 1};
    },

    addDocPaths : function*(ctx){
        let params = ctx.params, id = params.id, deviceInfo = this.deviceIndex[id],
            dao, results;
        if(!deviceInfo){
            return {code : -1, msg : 'no device'};
        }

        dao = daoUtil.getDao(deviceInfo.daoName);
        results = yield dao.getAllJspData();

        results = results || [];
        return {code : 8, paths : results.map(r => r.url)};
    },

    onUpdateDoc : function(appName, path){
        let targetDeviceIds = Object.keys(this.deviceIndex).filter(id => this.deviceIndex[id].daoName === appName);

        targetDeviceIds.forEach(id => {
            let jspData = this.finalJspData[id];
            if(jspData){
                delete jspData[path];
            }

            this.socketSender(this.deviceIndex[id].socketId, 8, {paths : [path]});
        });
    },

    addDeveloper : function(jspClient, deviceId){
        this.jspDevIndex[deviceId] = this.jspDevIndex[deviceId] || [];
        this.jspDevIndex[deviceId].push(jspClient);
    },

    removeDeveloper : function(jspClient){
        let deviceId = jspClient.nowDeviceId;

        if(!deviceId){
            return;
        }
        let clientPos = this.jspDevIndex[deviceId].indexOf(jspClient),
            offlineClients = this.offlineIndex[deviceId];
        !!~clientPos && this.jspDevIndex[deviceId].splice(clientPos, 1);

        if(offlineClients){
            let offlinePos = offlineClients.indexOf(jspClient);
            !!~offlinePos && offlineClients.splice(offlinePos, 1);
            if(!offlineClients.length){
                delete this.offlineIndex[deviceId];
            }
        }
    },

    searchJspData : function*(ctx){
        let params = ctx.params || {}, path = params.path, id = params.id, jobId = params.jobId,
            finalData = this.finalJspData[id], deviceInfo = this.deviceIndex[id],
            dao, targetIfObj, result;
        if(!deviceInfo){
            return {code : -1, msg : 'no device', jobId};
        }

        finalData = finalData || (this.finalJspData[id] = {});
        if(result = finalData[path]){
            let resData = {code : 2, jobId, result : result.data, page : result.page};
            if(result.setCookies){
                resData.setCookies = result.setCookies;
            }
            return resData;
        }else{
            dao = daoUtil.getDao(deviceInfo.daoName);
            targetIfObj = yield dao.getJspDataByUrl(path);

            if(targetIfObj){
                result = targetIfObj.resultDemo;
                if(typeof result === 'string'){
                    try{result = JSON.parse(result);}catch(e){}
                }
                finalData[path] = {data : result, page : targetIfObj.resultPage};

                return {code : 2, jobId, result, page : targetIfObj.resultPage};
            }else{
                return {code : 3, jobId};
            }
        }

    },

    uploadJspData : function(ctx){
        let params = ctx.params || {}, path = params.path, data = params.data, page = params.page,
            cookies = params.cookies, id = params.id, finalData = this.finalJspData[id];

        finalData = finalData || (this.finalJspData[id] = {});

        finalData[path] = {data, page, cookies};

        this.jspDevIndex[id] && this.jspDevIndex[id]
            .forEach(c => c.pushJspDataShow({data, page, path, cookies}));

        return {code : 1}
    },

    offline : function(ctx){
        let params = ctx.params, socketId = params && params._socketId,
            deviceInfo = this.socketIndex[socketId];

        if(deviceInfo){
            delete this.socketIndex[socketId];
            delete this.deviceIndex[deviceInfo.id];
            delete this.finalJspData[deviceInfo.id];

            if(this.jspDevIndex[deviceInfo.id]){
                this.offlineIndex[deviceInfo.id] = this.jspDevIndex[deviceInfo.id];
                delete this.jspDevIndex[deviceInfo.id];

                this.offlineIndex[deviceInfo.id].forEach(client => client.notifyOffLine());
            }

        }

        return 'ok';
    }
});

class JspDevClient {
    constructor(socket){
        let _this = this, commandName;
        this.socket = socket;
        this.userName = null;
        this.jspApiService = null;
        this.nowDeviceId = null;
        this.lastPage = null;

        Object.getOwnPropertyNames(Object.getPrototypeOf(this))
            .forEach(commandName => {
            if(commandName.startsWith('cmd')){
                socket.on(commandName, _this[commandName].bind(_this));
            }
        });
    }

    getService(){
        if(!this.jspApiService){
            this.jspApiService = beanUtil.getBean('JspApiService');
        }

        return this.jspApiService;
    }

    destroy(){
        this.socket.removeAllListeners();
        this.getService().removeDeveloper(this);
    }

    notifyOffLine(){
        this.socket.emit('deviceOffLine');
    }

    notifyOnLine(){
        this.socket.emit('deviceOnLine');
    }

    pushJspDataShow(dataInfo){
        if(this.lastPage !== dataInfo.page){
            this.socket.emit('jspDataUpdate', dataInfo);
            this.lastPage = dataInfo.page;
        }else{
            this.socket.emit('jspDataUpdateWithoutPage', dataInfo);
        }
    }

    cmdSetUserName(userName){
        this.userName = userName;
    }

    cmdMacMatch(macAddress){
        let service = this.getService(),
            targetDevice = service.deviceIndex[macAddress];
        if(targetDevice){
            this.nowDeviceId = macAddress;
            service.addDeveloper(this, this.nowDeviceId);
            this.socket.emit('macMatched', targetDevice);
        }else{
            this.socket.emit('notice', {text : '没有相关设备', level : 'warning'});
        }
    }

    cmdApplyNewData(dataInfo){
        let service = this.getService(), id = this.nowDeviceId, jspDatas;
        jspDatas = service.finalJspData[id] ;
        if(!jspDatas){
            jspDatas = service.finalJspData[id] = {};
        }

        jspDatas[dataInfo.path] = {data : dataInfo.data, page : dataInfo.page};
    }

    cmdApplyNewCookie(dataInfo){
        let service = this.getService(), id = this.nowDeviceId, jspDatas;
        jspDatas = service.finalJspData[id] ;
        if(jspDatas && jspDatas[dataInfo.path]){
            jspDatas[dataInfo.path].setCookies = dataInfo.setCookies;
        }
    }

    cmdClearPageData(dataInfo){
        let service = this.getService(), id = this.nowDeviceId, jspDatas;
        jspDatas = service.finalJspData[id] ;

        if(jspDatas && jspDatas[dataInfo.path]){
            delete jspDatas[dataInfo.path];
        }
    }

    cmdApplyNewPage(dataInfo){
        let service = this.getService(), id = this.nowDeviceId, jspDatas;
        jspDatas = service.finalJspData[id] ;

        if(jspDatas && jspDatas[dataInfo.path]){
            jspDatas[dataInfo.path].page = dataInfo.page;
        }
    }
}

JspApiService.processIO = function(socketIO){

    socketIO.on('connection', function(socket){
        let jspClient = new JspDevClient(socket);
        socket.on('disconnect', function(){
            jspClient.destroy();
        });
        socket.on('error', function(e){
            socket.emit('notice', {text: 'Oops! ' + e.message, level : 'error'});
        });
    });
};

module.exports = beanUtil.makeBeanDefineObj(JspApiService, null, ['processIO']);